/**
 * 
 */
package org.softTank.BC.manager.util;

import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.log4j.Logger;
import org.softTank.BC.rs.bean.ParameterBean;
import org.softTank.BcpModel.BcpControlType;
import org.softTank.BcpModel.BcpPartnerLink;
import org.softTank.BcpModel.BcpQName;
import org.softTank.BcpModel.BcpVariable;
import org.softTank.BcpModel.partnerLinkType.MessageType;
import org.softTank.BcpModel.partnerLinkType.Operation;
import org.softTank.BcpModel.partnerLinkType.PartnerLinkType;
import org.softTank.BcpModel.partnerLinkType.PortType;
import org.softTank.BcpModel.partnerLinkType.Role;
import org.softTank.schema.bean.Element;
import org.softTank.wsdlParser.bean.Definitions;
import org.softTank.wsdlParser.bean.Message;
import org.softTank.wsdlParser.bean.ParamType;
import org.softTank.wsdlParser.bean.Types;

/**
 * @author Yuzone
 *
 */
public class OperationParserImpl implements PartnerLinkGenerator,
		VariableGenerator, OperationParser {

	private String operation;
	private String serviceId;
	private Definitions def;
	protected Logger logger;

	public OperationParserImpl() {
		logger = Logger.getLogger(this.getClass());
	}

	public List<ParameterBean> getParameterList(String serviceID,
			String operationName) {
		this.operation = operationName;
		this.serviceId = serviceID;
		logger
				.debug("Start to get parameter list of: " + operationName
						+ "...");
		def = ServiceParser.definitionMap.get(serviceID);

		List<ParameterBean> pbList = new ArrayList<ParameterBean>();
		org.softTank.wsdlParser.bean.Operation op = this.findOperation();
		if (op.getInput() != null) {
			logger.debug("Start to create parameterBean for input message...");
			pbList.addAll(createParameterBean(op.getInput()));
		}
		if (op.getOutput() != null) {
			logger.debug("Start to create parameterBean for output message...");
			pbList.addAll(createParameterBean(op.getOutput()));
		}
		logger.debug("Return the parameter list.");

		return pbList;
	}

	private List<ParameterBean> createParameterBean(ParamType opParam) {
		List<ParameterBean> pbList = new ArrayList<ParameterBean>();
		Message message = findMessage(opParam.getMessage());
		if (message != null) {
			logger.debug("Found message: " + message.getName());
			Element element = findElement(message.getPart().get(0).getElement());
			if (element != null) {
				logger.debug("Found element: " + element.getName());
				List<Element> eList = element.getComplexType().getSequence()
						.getElement();
				for (Element e : eList) {
					ParameterBean pb = new ParameterBean();
					pb.setElement(element.getName().getLocalPart());
					pb.setElementNamespace(message.getPart().get(0)
							.getElement().getNamespaceURI());
					pb.setName(e.getName().getLocalPart());
					pb.setPart(message.getPart().get(0).getName()
							.getLocalPart());
					pb.setVariable(message.getName().getLocalPart());
					pbList.add(pb);
					logger.debug("Added a parameterBean to list!");
				}
			}
		}

		return pbList;
	}

	private Element findElement(QName element) {
		Types types = def.getTypes();
		List<Element> eList = types.getSchema().getElement();
		for (Element e : eList) {
			if (e.getName().toString().equalsIgnoreCase(element.getLocalPart()))
				return e;
		}
		return null;
	}

	private Message findMessage(QName message) {
		List<Message> mList = def.getMessage();
		for (Message m : mList) {
			if (m.getName().getLocalPart().equalsIgnoreCase(
					message.getLocalPart()))
				return m;
		}

		return null;
	}

	public List<BcpVariable> getVariable(String serviceID, String operationName) {
		this.operation = operationName;
		this.serviceId = serviceID;
		logger.debug("Start to get Variable for: " + operationName + "...");
		def = ServiceParser.definitionMap.get(serviceID);
		List<BcpVariable> bvList = new ArrayList<BcpVariable>();
		Operation op = this.getOperation();
		if (op.getInputType() != null) {
			BcpVariable bv = new BcpVariable();
			bv.setMessageType(op.getInputType());
			bv.setName(op.getInputType().getName().getLocalPart());
			bvList.add(bv);
		}
		if (op.getOutputType() != null) {
			BcpVariable bv = new BcpVariable();
			bv.setMessageType(op.getOutputType());
			bv.setName(op.getOutputType().getName().getLocalPart());
			bvList.add(bv);
		}
		logger.debug("Return the Variable list of: " + operationName + ".");

		return bvList;
	}

	public BcpPartnerLink getPartnerLink(String service, String name,
			int type) {
		this.operation = name;
		this.serviceId = service;
		logger.debug("Start to get PartnerLink for: " + name + "...");

		BcpPartnerLink bpl = new BcpPartnerLink();
		def = ServiceParser.definitionMap.get(service);
		PartnerLinkType plt = getPartnerLinkType(type);
		if (findPartnerLinkType().getRole().size() == 1) {
			if (type == BcpControlType.INVOKE) {
				bpl.setPartnerRole(plt.getRoleA().getName().getLocalPart());
			} else {
				bpl.setMyRole(plt.getRoleA().getName().getLocalPart());
			}
		} else {
			bpl.setMyRole(plt.getRoleA().getName().getLocalPart());
			bpl.setPartnerRole(plt.getRoleB().getName().getLocalPart());
		}
		bpl.setName(plt.getName().getLocalPart());
		bpl.setPartnerLinkType(plt);
		logger.debug("Return the PartnerLink of: " + name + ".");

		return bpl;
	}

	private PartnerLinkType getPartnerLinkType(int type) {
		logger.debug("Create partnerLink...");
		PartnerLinkType plt = new PartnerLinkType();
		plt.setName(new BcpQName(findPartnerLinkType().getName()));
		plt.setTnsLocation(ServiceParser.wsdlMap.get(serviceId));
		plt.setTargetNamespace(def.getTargetNamespace());
		int count = findPartnerLinkType().getRole().size();
		if (count == 1) {
			Role role = getRole(findPartnerLinkType()).get(0);
			plt.setRoleA(role);
		} else {
			if (type == BcpControlType.INVOKE) {
				if (findRole(findPortType()).getName().getLocalPart().equals(
						getRole(findPartnerLinkType()).get(0).getName())) {
					plt.setRoleA(getRole(findPartnerLinkType()).get(1));
					plt.setRoleB(getRole(findPartnerLinkType()).get(0));
				} else {
					plt.setRoleA(getRole(findPartnerLinkType()).get(0));
					plt.setRoleB(getRole(findPartnerLinkType()).get(1));
				}
			} else {
				if (findRole(findPortType()).getName().getLocalPart().equals(
						getRole(findPartnerLinkType()).get(0).getName())) {
					plt.setRoleA(getRole(findPartnerLinkType()).get(0));
					plt.setRoleB(getRole(findPartnerLinkType()).get(1));
				} else {
					plt.setRoleA(getRole(findPartnerLinkType()).get(1));
					plt.setRoleB(getRole(findPartnerLinkType()).get(0));
				}
			}
		}
		logger.debug("Create partnerLink completely!");

		return plt;
	}

	private List<Role> getRole(org.softTank.wsdlParser.bean.PartnerLinkType plt) {
		List<Role> rList = new ArrayList<Role>();
		for (org.softTank.wsdlParser.bean.Role r : plt.getRole()) {
			Role role = new Role();
			role.setName(new BcpQName(r.getName()));
			role.setTargetNamespace(def.getTargetNamespace());
			role.setPortType(getPortType(r.getPortType()));
			rList.add(role);
		}

		return rList;
	}

	private PortType getPortType(QName portType) {
		PortType pt = new PortType();
		pt.setName(new BcpQName(portType));
		pt.setTargetNamespace(def.getTargetNamespace());
		pt.setTnsLocation(ServiceParser.wsdlMap.get(serviceId));
		if (portType.getLocalPart().equals(
				findPortType().getName().getLocalPart())) {
			pt.getOperation().add(getOperation());
		}

		return pt;
	}

	private Operation getOperation() {
		Operation o = new Operation();
		org.softTank.wsdlParser.bean.Operation ob = findOperation();
		o.setName(new BcpQName(ob.getName()));
		if (ob.getInput() != null && ob.getInput().getMessage() != null) {
			logger
.debug("Search input message for: "
							+ o.getName().getLocalPart());
			o.setInputType(getMessageType(ob.getInput().getMessage()));
		}
		if (ob.getOutput() != null && ob.getOutput().getMessage() != null) {
			logger
.debug("Search output message for: "
							+ o.getName().getLocalPart());
			o.setOutputType(getMessageType(ob.getOutput().getMessage()));
		}

		return o;
	}

	private MessageType getMessageType(QName message) {
		List<Message> mList = def.getMessage();
		for (Message msg : mList) {
			if (msg.getName().getLocalPart().equalsIgnoreCase(
					message.getLocalPart())) {
				MessageType mt = new MessageType();
				mt.setTargetNamespace(def.getTargetNamespace());
				mt.setTnsLocation(ServiceParser.wsdlMap.get(serviceId));
				mt.setName(new BcpQName(msg.getName()));
				logger.debug("Find message: " + msg.getName().toString());
				return mt;
			}
		}
		logger.debug("No such Message definition is found!");

		return null;
	}

	private org.softTank.wsdlParser.bean.PartnerLinkType findPartnerLinkType() {
		return findPartnerLinkType(findPortType());
	}

	private org.softTank.wsdlParser.bean.Operation findOperation() {
		logger.debug("Looking for operation: " + operation + "...");
		List<org.softTank.wsdlParser.bean.PortType> ptList = def.getPortType();
		for (org.softTank.wsdlParser.bean.PortType pt : ptList) {
			List<org.softTank.wsdlParser.bean.Operation> oList = pt
					.getOperation();
			for (org.softTank.wsdlParser.bean.Operation o : oList) {
				logger.debug("find operation: " + o.getName());
				if (o.getName().getLocalPart().equals(operation))
					return o;
			}
		}
		logger.debug("No such operation element!");

		return null;
	}

	private org.softTank.wsdlParser.bean.PortType findPortType() {
		logger.debug("Looking for portType of: " + operation + "...");
		List<org.softTank.wsdlParser.bean.PortType> ptList = def.getPortType();
		for (org.softTank.wsdlParser.bean.PortType pt : ptList) {
			List<org.softTank.wsdlParser.bean.Operation> oList = pt
					.getOperation();
			for (org.softTank.wsdlParser.bean.Operation o : oList) {
				logger.debug("find operation: " + o.getName());
				if (o.getName().getLocalPart().equals(operation))
					return pt;
			}
		}
		logger.debug("No related portType element!");

		return null;
	}

	private org.softTank.wsdlParser.bean.Role findRole(
			org.softTank.wsdlParser.bean.PortType portType) {
		if (portType == null)
			return null;
		List<org.softTank.wsdlParser.bean.PartnerLinkType> pltList = def
				.getPartnerLinkType();
		for (org.softTank.wsdlParser.bean.PartnerLinkType plt : pltList) {
			List<org.softTank.wsdlParser.bean.Role> rList = plt.getRole();
			for (org.softTank.wsdlParser.bean.Role r : rList) {
				if (r.getPortType().getLocalPart().equals(
						portType.getName().getLocalPart()))
					return r;
			}
		}
		logger.debug("No related role element!");

		return null;
	}

	private org.softTank.wsdlParser.bean.PartnerLinkType findPartnerLinkType(
			org.softTank.wsdlParser.bean.PortType portType) {
		if (portType == null)
			return null;
		List<org.softTank.wsdlParser.bean.PartnerLinkType> pltList = def
				.getPartnerLinkType();
		for (org.softTank.wsdlParser.bean.PartnerLinkType plt : pltList) {
			List<org.softTank.wsdlParser.bean.Role> rList = plt.getRole();
			for (org.softTank.wsdlParser.bean.Role r : rList) {
				if (r.getPortType().getLocalPart().equals(
						portType.getName().getLocalPart()))
					return plt;
			}
		}
		logger.debug("No related partnerLinkType element!");

		return null;
	}
}
